Skip to content

feat(sdk): add streaming extensions and response accumulator#75

Open
jezekra1 wants to merge 4 commits intomainfrom
sdk-streaming-refactor
Open

feat(sdk): add streaming extensions and response accumulator#75
jezekra1 wants to merge 4 commits intomainfrom
sdk-streaming-refactor

Conversation

@jezekra1
Copy link
Copy Markdown
Contributor

@jezekra1 jezekra1 commented Mar 20, 2026

Summary

Adds a delta-based streaming protocol using JSON Patch (RFC 6902) with a custom str_ins operation for efficient token-by-token text delivery to the UI.

Server (adk-py)

  • MessageAccumulator — 3-level state machine that collects string chunks, parts, and metadata into complete messages, generating incremental JSON Patch operations for the client
  • Extended JSON Patch (jsonpatch_ext) — adds a str_ins operation for optimized text streaming without full-value replacement on every token
  • StreamingExtensionServer/Client — extension negotiation via agent card capabilities, patch extraction from status update metadata, delta emission, and graceful fallback for non-streaming clients
  • Extension base refactoring — activation lifecycle improvements, consolidated DEFAULT_DEMAND_NAME constant (was duplicated in 5 files), improved metadata handling
  • RunContext improvements — extracted shared store preparation into _prepare_store_data, added store_sync for synchronous callers
  • New yield typeArtifactChunk for chunked artifact streaming with shared artifact IDs

Client (adk-ts)

  • TypeScript streaming extension types, Zod schemas, and STREAMING_EXTENSION_URI constant
  • Extension re-exports for clean public API

UI (adk-ui)

  • Patch application (streaming.ts) — JSON pointer resolution, support for replace/add/str_ins operations, optimized cloning (skips structuredClone for immutable primitives)
  • Client streaming integration — draft accumulation in streamingDraft, patch extraction from TaskStatusUpdateEvent metadata, replace-mode emission
  • Bug fix: text duplication on final message — when streaming was active, the final complete message was appended instead of replacing the streaming draft, causing duplicated text (e.g. "Hello! How can I assist you today?Hello! How can I assist you today?"). Now detects active streaming sessions and emits with replace: true
  • Performance: batched React state updates — collapsed multiple updateCurrentAgentMessage calls per streaming tick into a single call, reducing re-renders during high-frequency token streaming
  • Cleanup — removed dead commented-out proxy header code from adk-client.ts

Wire format

Streaming data is carried in TaskStatusUpdateEvent.metadata[STREAMING_EXTENSION_URI] as a message_update patch list with a message_id. Clients that don't negotiate streaming support receive the final complete message at TASK_STATE_COMPLETED — fully backward compatible.

Test plan

  • Unit tests for MessageAccumulator state machine (state transitions, patch output, flush)
  • Unit tests for extended JSON Patch str_ins operation
  • Unit tests for RunContext store methods
  • E2E streaming tests (patch application, message reconstruction, client fallback)
  • Updated yield tests for artifact chunking
  • mise run check passes (lint, typecheck, license)

@jezekra1 jezekra1 force-pushed the sdk-streaming-refactor branch 2 times, most recently from 9917354 to 7c4c222 Compare March 25, 2026 15:24
@jezekra1 jezekra1 marked this pull request as ready for review March 25, 2026 15:24
@jezekra1 jezekra1 requested review from PetrBulanek and tomkis March 25, 2026 15:26
@jezekra1 jezekra1 force-pushed the sdk-streaming-refactor branch 3 times, most recently from 0172097 to ad3c581 Compare March 26, 2026 08:06
@tomkis
Copy link
Copy Markdown
Contributor

tomkis commented Mar 26, 2026

@PetrBulanek can you please review the UI part?

…eaming support

Adds a delta-based streaming protocol using JSON Patch (RFC 6902) with a
custom `str_ins` operation for efficient token-by-token text delivery.

Server (adk-py):
- MessageAccumulator: 3-level state machine collecting string chunks,
  parts, and metadata into complete messages with incremental patches
- Extended JSON Patch (jsonpatch_ext): str_ins operation for optimized
  text streaming without full-value replacement
- StreamingExtensionServer/Client: extension negotiation, patch
  extraction, delta emission, and graceful fallback for non-streaming
  clients
- Refactored extension base: activation lifecycle, default demand
  constant consolidation (DEFAULT_DEMAND_NAME), and metadata handling
- RunContext: extracted shared store logic into _prepare_store_data,
  added store_sync for synchronous callers
- New yield types: ArtifactChunk for chunked artifact streaming

Client (adk-ts / adk-ui):
- TypeScript streaming extension types and Zod schemas
- UI patch application (streaming.ts): JSON pointer resolution,
  replace/add/str_ins ops with optimized cloning (skip structuredClone
  for primitives)
- Client streaming integration: draft accumulation, patch extraction
  from status update metadata, replace-mode emission
- Fix: text duplication on final message — detect active streaming
  session and emit with replace flag to prevent appending duplicate
  content
- Batched React state updates in AgentRunProvider to reduce re-renders
  during streaming
- Removed dead commented-out proxy header code from adk-client

Tests:
- Unit tests for MessageAccumulator state machine
- Unit tests for extended JSON Patch operations
- Unit tests for RunContext store methods
- E2E streaming tests: patch application, message reconstruction,
  client fallback behavior
- Updated yield tests for new artifact chunking

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Radek Ježek <radek.jezek@ibm.com>
@jezekra1 jezekra1 force-pushed the sdk-streaming-refactor branch from ad3c581 to f902343 Compare March 26, 2026 12:44
Signed-off-by: Petr Bulánek <bulanek.petr@gmail.com>
…rray pattern

Signed-off-by: Petr Bulánek <bulanek.petr@gmail.com>
…pping

Signed-off-by: Petr Bulánek <bulanek.petr@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants